using Gtk;

namespace Debins {
    public class PackageManager : Object {

        // === Public vars: ===================================================
        public bool isUpgradeable { get { return _isUpgradeable; } }
        public bool isDowngradeable { get { return _isDowngradeable; } }
        public bool isExists { get { return _isExists; } }
        
        // Package info
        public string packageInfo { get { return _packageInfo; }}
        public string packageVersion { get { return _packageVersion; } }
        public string packageName { get { return _packageName; } }
        public string fileName { get { return _fileName; } }
        public int64 packageSize { get { return _packageSize; } }
        public string packageExistingVersion { get { return _packageExistingVersion; } }

        // Installation info
        public signal void onOutput(string output);
        public signal void onFinished(int status);


        // === Private vars: ===================================================
        private bool _isUpgradeable = false;
        private bool _isDowngradeable = false;
        private bool _isExists = false;

        private string _packageInfo;
        private string _packageVersion;
        private string _packageName;
        private string _fileName;
        private int64 _packageSize;
        private string _packageExistingVersion;


        // Process command vars:
        private string[] cmd_installArr = {"aysu", "paket", "kur", "{DEBFILE}"};
        private string[] cmd_uninstallArr = {"aysu", "paket", "sil", "{PACKAGE}"};

        private string cmd_getPackageInfo = "aysu paket bil {DEBFILE}";
        private string cmd_getPackageExistingVersion = "paket bil {PACKAGE} | grep SURUM";
        private string cmd_checkIfPackageExists = "paket bil {PACKAGE} | grep SURUM";

        private string cmd_checkIfNewVersion = "/bin/sh -c \"test {VERSION} gt {CURRENTVERSION}\"";
        private string cmd_checkIfOldVersion = "/bin/sh -c \"test {VERSION} lt {CURRENTVERSION}\"";

        public PackageManager(File file) {
            _fileName = file.get_basename();
            var fileinfo = file.query_info("standard::*", FileQueryInfoFlags.NONE);
            _packageSize = fileinfo.get_size();

            cmd_installArr[3] = cmd_installArr[3].replace("{DEBFILE}", file.get_path());
            cmd_getPackageInfo = cmd_getPackageInfo.replace("{DEBFILE}", file.get_path());
            _packageInfo = getPackageInfo();

            refreshPackageInfo();
        }

        public void refreshPackageInfo() {
            
            cmd_checkIfNewVersion = "/bin/sh -c \"test {VERSION} gt {CURRENTVERSION}\"";
            cmd_checkIfOldVersion = "/bin/sh -c \"test {VERSION} lt {CURRENTVERSION}\"";

            cmd_uninstallArr[3] = cmd_uninstallArr[3].replace("{PACKAGE}", packageName);
            cmd_getPackageExistingVersion = cmd_getPackageExistingVersion.replace("{PACKAGE}", packageName);
            cmd_checkIfPackageExists = cmd_checkIfPackageExists.replace("{PACKAGE}", packageName);
            if( checkIfPackageExists()) {
                getPackageExistingVersion();

                cmd_checkIfNewVersion = cmd_checkIfNewVersion.replace("{VERSION}", packageVersion);
                cmd_checkIfNewVersion = cmd_checkIfNewVersion.replace("{CURRENTVERSION}", packageExistingVersion);
                cmd_checkIfOldVersion = cmd_checkIfOldVersion.replace("{VERSION}", packageVersion);
                cmd_checkIfOldVersion = cmd_checkIfOldVersion.replace("{CURRENTVERSION}", packageExistingVersion);

                checkIfNewVersion();
                checkIfOldVersion();
            }

            
            stdout.printf("isUpgradeable: %s\n", isUpgradeable.to_string());
            stdout.printf("isDowngradeable: %s\n", isDowngradeable.to_string());
            stdout.printf("isExists: %s\n", isExists.to_string());
            stdout.printf("packageName: %s\n", packageName);
            stdout.printf("packageVersion: %s\n", packageVersion);
            stdout.printf("packageExistingVersion: %s\n", packageExistingVersion);
            stdout.printf("packageSize: %d\n", (int)packageSize);
            
        }

        // -- Functions with processes:
        public void install() {
            try
            {               
                int processOut;
                int processIn;
                int processErr;
                Pid childPid;

                string[] spawnEnv = Environ.get();
                Process.spawn_async_with_pipes("/",
                    cmd_installArr,
                    spawnEnv,
                    SpawnFlags.SEARCH_PATH | SpawnFlags.LEAVE_DESCRIPTORS_OPEN | SpawnFlags.DO_NOT_REAP_CHILD,
                    null,
                    out childPid,
                    out processIn,
                    out processOut,
                    out processErr);

                // stdout:
                IOChannel output = new IOChannel.unix_new (processOut);
                output.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
                    if (condition == IOCondition.HUP) {
                        return false;
                    }

                    string line;
                    channel.read_line (out line, null, null);
                    onOutput(line);
                    return true;
                });

                ChildWatch.add(childPid, (pid, status) => {
                    // Triggered when the child indicated by child_pid exits
                    Process.close_pid (pid);
                    onFinished(status);
                });

            }
            catch (SpawnError e)
            {
                stderr.printf("%s\n", e.message);
            }
        }

        public void uninstall() {
            try
            {               
                int processOut;
                int processIn;
                int processErr;
                Pid childPid;

                string[] spawnEnv = Environ.get();
                Process.spawn_async_with_pipes("/",
                    cmd_uninstallArr,
                    spawnEnv,
                    SpawnFlags.SEARCH_PATH | SpawnFlags.LEAVE_DESCRIPTORS_OPEN | SpawnFlags.DO_NOT_REAP_CHILD,
                    null,
                    out childPid,
                    out processIn,
                    out processOut,
                    out processErr);

                // stdout:
                IOChannel output = new IOChannel.unix_new (processOut);
                output.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
                    if (condition == IOCondition.HUP) {
                        return false;
                    }

                    string line;
                    channel.read_line (out line, null, null);
                    onOutput(line);
                    return true;
                });

                ChildWatch.add(childPid, (pid, status) => {
                    // Triggered when the child indicated by child_pid exits
                    Process.close_pid (pid);
                    onFinished(status);
                });

            }
            catch (SpawnError e)
            {
                stderr.printf("%s\n", e.message);
            }
        }

        private string getPackageInfo(){
            string output, error;
            int status;
            try
            {                
                Process.spawn_command_line_sync(cmd_getPackageInfo,
                    out output,
                    out error,
                    out status);
            }
            catch (SpawnError e)
            {
                stderr.printf("%s\n", e.message);
            }
            
            string[] lines = output.split("\n");
            foreach (var line in lines) {
                if ( line.length == 0 ) continue;
                //stdout.printf("Line: %s\n", line);
                string[] splitted = line.split(":");
                splitted[0] = splitted[0].strip();
                if (splitted[0] == "Package") {
                    _packageName = splitted[1].strip();
                } else if (splitted[0] == "Version") {
                    _packageVersion = splitted[1].strip();
                }
            }
    
            return output;
        }

        string getPackageExistingVersion(){
            string output, error;
            int status;
            try
            {
                Process.spawn_command_line_sync(cmd_getPackageExistingVersion,
                    out output,
                    out error,
                    out status);

                string[] lines = output.split("\n");
                foreach (var line in lines) {
                    if ( line.length == 0 ) continue;
                    //stdout.printf("Line: %s\n", line);
                    string[] splitted = line.split(":");
                    splitted[0] = splitted[0].strip();
                    if (splitted[0] == "Version") {
                        _packageExistingVersion = splitted[1].strip();
                    }
                }
            }
            catch (SpawnError e)
            {
                stderr.printf("ERROR: %s\n", e.message);
            }
    
            return _packageExistingVersion;
        }

        bool checkIfPackageExists(){
            string output, error;
            int status;
            try
            {                
                Process.spawn_command_line_sync(cmd_checkIfPackageExists,
                    out output,
                    out error,
                    out status);
                
                _isExists = status ==  0;
            }
            catch (SpawnError e)
            {
                stderr.printf("%s\n", e.message);
            }
    
            return _isExists;
        }

        bool checkIfNewVersion() {
            string output, error;
            int status;
            try
            {                
                Process.spawn_command_line_sync(cmd_checkIfNewVersion,
                    out output,
                    out error,
                    out status);
            }
            catch (SpawnError e)
            {
                stderr.printf("%s\n", e.message);
            }

            _isUpgradeable = status == 0;
    
            return _isUpgradeable;
        }

        bool checkIfOldVersion() {
            string output, error;
            int status;
            try
            {                
                Process.spawn_command_line_sync(cmd_checkIfOldVersion,
                    out output,
                    out error,
                    out status);
            }
            catch (SpawnError e)
            {
                stderr.printf("%s\n", e.message);
            }

            _isDowngradeable = status == 0;
    
            return _isDowngradeable;
        }
    }
}
